/*
 * list.c 
 * 
 * List manipulations
 *
 * Copyright (C) 2020 Long-Horn
 *
 */
 
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

#include "list.h"

#ifdef DEBUG_PRINT
	#define debug fprintf
#else
	#define debug(...) do { /* ... */ } while(0)
#endif

#define FREE(x) if(x) { free(x); x=NULL; }

list_s *list_new(char *name)
{
    list_s *list = calloc(1, sizeof(list_s));
    if (!list)
        return NULL;
        
    node_s *node = calloc(1, sizeof(node_s));
    if (!node) {
        free(list);
        return NULL;
    }
    
    if (pthread_mutex_init(&list->mutex,NULL) != 0) {
        perror("list_new: mutex_init");
        free(list);
        free(node);
        return NULL;
    }
    
    list->name = strdup(name);

    node->data = NULL;
    node->next = node;
    node->prev = node;

    list->head = node;	// Point to first
    list->tail = node;	// Set prev element

    debug(stderr, "list_new(%s) l=%p l->head=%p l->tail=%p\n", list->name, list, list->head, list->tail);
    return list;
}

void list_free(list_s **plist, void (*free_func)(void *), void (*freep_func)(void **))
{
    list_s *list = *plist;
    if (!list)
	return;
	
    debug(stderr, "list_free(%s)\n", list->name);
    node_s *node, *tmp;
    
    list_lock(list);
    list_for_each(list, node, tmp) {
        if (free_func && node->data)
            free_func(node->data);
        if (freep_func && node->data)
	        freep_func(&node->data);
	
        list_del_unlocked(list, &node);
    }
    FREE(list->head);
    list_unlock(list);
    pthread_mutex_destroy(&list->mutex);
    FREE(list->name);
    FREE(*plist);
}

void list_lock(list_s *list)
{
    pthread_mutex_lock(&list->mutex);
}

void list_unlock(list_s *list)
{
    pthread_mutex_unlock(&list->mutex);
}

void list_add(list_s *list, void *data)
{
    if (!list) {
        debug(stderr, "list_add(), list == NULL\n");
        return;
    }
    
    if (!data) {
        debug(stderr, "list_add(%s), data == NULL\n", list->name);
        return;
    }

    node_s *node = calloc(1, sizeof(node_s));
    if (!node) {
        debug(stderr, "list_add(%s), can't alloc node\n", list->name);
        return;
    }
    
    node->data = data;
    
    //	if (strcmp(list->name, "clients") == 0 || strcmp(list->name, "r->clients") == 0)
    debug(stderr, "list_add(%s), node=%p data=%p\n", list->name, node, node->data);
    
    list_lock(list);

    node->prev = list->tail;
    node->next = list->head;

    list->tail->next = node;
    list->tail = node;
    list->head->prev = node;
    
    list->items++;
    list_unlock(list);
}

void list_dump(list_s *list)
{
    if (list == NULL) {
        fprintf(stderr, "list_dump(), list is null\n");
        return;
    }
    fprintf(stderr, "list_dump(%s), nodes:%d\n", list->name, list->items);
    node_s *node, *tmp;
    int i = 0;
    list_for_each(list, node, tmp) {
    fprintf(stderr, "Node:%d Head:%p Tail:%p Node:%p NodeNext:%p NodePrev:%p Data:%p -> %s\n",
            i++, list->head, list->tail, node, node->next, node->prev, node->data, (char *)node->data);
    }
}

int list_del_unlocked(list_s *list, node_s **node)
{
    if (!list || !*node)
        return 0;
        
    node_s *prev = (*node)->prev;
    node_s *next = (*node)->next;
    
    next->prev = prev;
    prev->next = next;
    if (*node == list->tail)
        list->tail = prev;
    
    list->items--;
        
    FREE(*node);
    return 1;
}

int list_del(list_s *list, node_s **node)
{
    if (!list || !*node)
        return 0;
    
    list_lock(list);
    int ret = list_del_unlocked(list, node);
    list_unlock(list);
    
    return ret;
}

int list_del_entry(list_s *list, void *data)
{
    int ret = 0;
    
    if (!list || !data)
        return 0;
    
    debug(stderr, "list_del_entry(%s, %p)\n", list->name, data);
    list_lock(list);
    node_s *node, *tmp;
    list_for_each(list, node, tmp) {
        if (node->data == data) {
            ret = list_del_unlocked(list, &node);
            break;
        }
    }
    
    list_unlock(list);
    return ret;
}
